﻿using Microsoft.QualityTools.Testing.Emulators;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Fakes;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace UnitTest
{
    public class Context : IDisposable
    {
        EmulationMode _emulationMode;
        public SPSite Site { get; private set; }
        public SPWeb Web { get; private set; }
        public SPList List { get; private set; }
        public SPRegionalSettings RegionalSettings { get; private set; }
        public Dictionary<ShimSPListItem, Hashtable> _properties = new Dictionary<ShimSPListItem, Hashtable>();

        public Context(EmulationMode _emulationMode)
        {
            this._emulationMode = _emulationMode;

            this.Site = new SPSite("http://spentity");
            if (this._emulationMode == EmulationMode.Enabled)
                this.Prepare();
            this.Web = this.Site.OpenWeb();

            this.List = this.Web.GetList("/Lists/AllFields");

            this.RegionalSettings = this.Web.RegionalSettings;
        }

        public void Dispose()
        {
            this._properties.Clear();
            if (this._emulationMode == EmulationMode.Passthrough)
            {
                this.Web.Dispose();
                this.Site.Dispose();
            }
        }

        private void Prepare()
        {
            var site = new ShimSPSite(this.Site);
            site.IDGet = () => Guid.NewGuid();
            site.OpenWeb = () => PrepareWeb(site);
        }

        private SPWeb PrepareWeb(ShimSPSite site)
        {
            var web = new ShimSPWeb();
            web.IDGet = () => Guid.NewGuid();
            web.SiteGet = () => site.Instance;
            web.GetListString = (url) => PrepareList(web);
            web.RegionalSettingsGet = () =>
            {
                var settings = new ShimSPRegionalSettings();
                return settings.Instance;
            };
            web.SiteUsersGet = () => PrepareUsers(web);
            return web.Instance;
        }

        private SPUserCollection PrepareUsers(ShimSPWeb web)
        {
            var users = new ShimSPUserCollection();
            users.GetByIDNoThrowInt32 = (id) =>
                {
                    var user = new ShimSPUser();
                    user.IDGet = () => 1;
                    user.NameGet = () => "Windie Chai";
                    return user.Instance;
                };
            return users.Instance;
        }

        private SPList PrepareList(ShimSPWeb web)
        {
            var list = new ShimSPList();
            list.IDGet = () => Guid.NewGuid();
            list.ParentWebGet = () => web.Instance;
            list.GetItemByIdInt32 = (id) => PrepareItem(id, list);
            list.AddItem = () => PrepareItem(ItemIds.Empty, list);
            return list.Instance;
        }

        private SPListItem PrepareItem(int id, ShimSPList list)
        {
            var item = new ShimSPListItem();
            item.ParentListGet = () => list.Instance;
            item.WebGet = () => list.Instance.ParentWeb;
            item.IDGet = () => id;
            item.UniqueIdGet = () => Guid.NewGuid();
            item.TitleGet = () => item.Instance[SPBuiltInFieldId.Title] as string;
            item.ItemGetGuid = (guid) =>
            {
                if (guid == SPBuiltInFieldId.Title)
                    return "title";
                else
                    return null;
            };
            item.ItemSetGuidObject = (guid, value) =>
            {
                if (guid == SPBuiltInFieldId.Title)
                    item.Instance["Title"] = value;
            };
            item.FieldsGet = () => PrepareFields(item);
            item.ItemGetString = (name) => GenerateFieldValue(name, item);
            item.ItemSetStringObject = (name, value) => SetFieldValue(name, value, item);
            item.PropertiesGet = () =>
            {
                if (!this._properties.ContainsKey(item))
                    this._properties[item] = new Hashtable();
                return this._properties[item];
            };
            return item.Instance;
        }

        private object SetFieldValue(string name, object value, ShimSPListItem item)
        {
            item.Instance.Properties[name] = value;
            return value;
        }

        private SPFieldCollection PrepareFields(ShimSPListItem item)
        {
            var fields = new ShimSPFieldCollection();
            Microsoft.QualityTools.Testing.Fakes.FakesDelegates.Func<string, SPField> GetField = (name) =>
            {
                ShimSPField.ConstructorSPFieldCollectionString = (self, fieldCollection, fieldName) => { };

                if (name == "CustomField")
                    return new CustomField(fields.Instance, "CustomField");

                var field = new ShimSPField();
                switch (name)
                {
                    case "Int":
                        field.FieldValueTypeGet = () => typeof(double);
                        break;
                    case "Double":
                        field.FieldValueTypeGet = () => typeof(double);
                        break;
                    case "Money":
                        field.FieldValueTypeGet = () => typeof(double);
                        break;
                    case "Date":
                        field.FieldValueTypeGet = () => typeof(DateTime);
                        break;
                    case "DateTime":
                        field.FieldValueTypeGet = () => typeof(DateTime);
                        break;
                    case "SingleLine":
                        field.FieldValueTypeGet = () => typeof(string);
                        break;
                    case "MultipleLine":
                        field.FieldValueTypeGet = () => typeof(string);
                        break;
                    case "Bool":
                        field.FieldValueTypeGet = () => typeof(bool);
                        break;
                    case "Link":
                        field.FieldValueTypeGet = () => typeof(SPFieldUrlValue);
                        break;
                    case "Image":
                        field.FieldValueTypeGet = () => typeof(SPFieldUrlValue);
                        break;
                    case "Choice":
                        field.FieldValueTypeGet = () => typeof(string);
                        break;
                    case "MultipleChoice":
                        field.FieldValueTypeGet = () => typeof(SPFieldMultiChoiceValue);
                        break;
                    case "User":
                        field.FieldValueTypeGet = () => typeof(SPFieldUserValue);
                        break;
                    case "Users":
                        field.FieldValueTypeGet = () => typeof(SPFieldUserValueCollection);
                        break;
                    case "Lookup":
                        field.FieldValueTypeGet = () => typeof(SPFieldLookupValue);
                        break;
                    case "MultipleLookup":
                        field.FieldValueTypeGet = () => typeof(SPFieldLookupValueCollection);
                        break;
                    case "NotSupportField":
                        field.FieldValueTypeGet = () => typeof(NotSupportFieldValue);
                        break;
                }
                return field.Instance;
            };
            fields.GetFieldByInternalNameString = GetField;
            fields.ItemGetString = GetField;
            return fields.Instance;
        }

        private object GenerateFieldValue(string name, ShimSPListItem item)
        {
            if (item.Instance.Properties.ContainsKey(name))
                return item.Instance.Properties[name];

            switch (item.Instance.ID)
            {
                case ItemIds.Empty:
                    return GetFieldValueForEmptyItem(name, item);
                default:
                    return GetFieldValueForNormalItem(name, item);
            }
        }

        private object GetFieldValueForNormalItem(string name, ShimSPListItem item)
        {
            switch (name)
            {
                case "Int": return 123.00;
                case "Double": return 123.00;
                case "Money": return 123.00;
                case "Date": return new DateTime(2013, 4, 5);
                case "DateTime": return new DateTime(2013, 4, 5, 6, 7, 8);
                case "SingleLine": return "Single Line String.";
                case "MultipleLine": return "Multiple Line String.\r\nSecond Line.";
                case "Bool": return true;
                case "Link": return new SPFieldUrlValue() { Url = "http://coding.windstyle.cn", Description = "Chai is Coding..." };
                case "Image": return new SPFieldUrlValue() { Url = "https://www.google.com/images/srpr/logo4w.png", Description = "Google's Logo" };
                case "Choice": return "Option #2";
                case "MultipleChoice": var multiChoiceValue = new SPFieldMultiChoiceValue();
                    multiChoiceValue.Add("Option #1");
                    multiChoiceValue.Add("Option #2");
                    multiChoiceValue.Add("Option #3");
                    return multiChoiceValue.ToString();
                case "User": return new SPFieldUserValue(item.Instance.Web, 1, "Windie Chai").ToString();
                case "Users": return new SPFieldUserValueCollection() 
                    { 
                        new SPFieldUserValue(item.Instance.Web, 1, "Windie Chai"),
                        new SPFieldUserValue(item.Instance.Web, 2, "Kaneboy")
                    };
                case "Lookup": return new SPFieldLookupValue(1, "Windie Chai").ToString();
                case "MultipleLookup": return new SPFieldLookupValueCollection() 
                    { 
                        new SPFieldLookupValue(1, "Windie Chai"),
                        new SPFieldLookupValue(2, "Kaneboy")
                    };
                case "NotSupportField": return new NotSupportFieldValue();
                case "CustomField": return new CustomFieldValue("Windie Chai");
                default: return null;
            }
        }

        private object GetFieldValueForEmptyItem(string name, ShimSPListItem item)
        {
            return null;
        }

        public static class ItemIds
        {
            public const int Empty = 1;
            public const int Normal = 2;
        }
    }
}
